package com.zyb.dfs.backupnode.server;

import com.alibaba.fastjson.JSONObject;

import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 负责管理内存中的文件目录树的核心组件
 *
 * @author zhonghuashishan
 */
public class FSDirectory {

    /**
     * 内存中的文件目录树
     */
    private INodeDirectory dirTree;

    /**
     * 文件目录树的读写锁
     */
    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    /**
     * 记录当前文件目录树更新到哪个txid了
     */
    private long maxTxid;

    /**
     * 读写锁加锁获释放锁
     */
    public void writeLock() {
        lock.writeLock().lock();
    }

    public void writeUnLock() {
        lock.writeLock().unlock();
    }

    public void readLock() {
        lock.readLock().lock();
    }

    public void readUnLock() {
        lock.readLock().unlock();
    }

    public FSDirectory() {
        this.dirTree = new INodeDirectory("/");
    }

    /**
     * 以json格式获取到fsimage内存元数据
     *
     * @return
     */
    public FSImage getFSImageByJson() {
        FSImage fsImage = null;
        try {
            readLock();
            String fsimageJson = JSONObject.toJSONString(dirTree);
            fsImage = new FSImage(maxTxid, fsimageJson);
            // 当前这份元数据最大的txid是多少，这样我们才知道fsimage到哪个txid了
        } finally {
            readUnLock();
        }
        return fsImage;
    }

    public void setMaxTxid(long maxTxid) {
        this.maxTxid = maxTxid;
    }

    /**
     * 创建文件
     * @param filename 文件名
     * @return
     */
    public Boolean create(String filename) {
        // /image/product/img001.jpg
        // 其实完全可以把前面的路径部分截取出来，去找对应的目录
        synchronized(dirTree) {
            String[] splitedFilename = filename.split("/");
            String realFilename = splitedFilename[splitedFilename.length - 1];

            INodeDirectory parent = dirTree;

            for(int i = 0; i < splitedFilename.length - 1; i++) {
                if(i == 0) {
                    continue;
                }

                INodeDirectory dir = findDirectory(parent, splitedFilename[i]);

                if(dir != null) {
                    parent = dir;
                    continue;
                }

//                INodeFile child = new INodeFile(splitedFilename[i]);
//                parent.addChild(child);
//                parent = child;
            }

            // 此时就已经获取到了文件的上一级目录
            // 可以查找一下当前这个目录下面是否有对应的文件了
            if(existFile(parent, realFilename)) {
                return false;
            }

            // 真正的在目录里创建一个文件出来
            //INodeFile file = new INodeFile(realFilename);
            //parent.addChild(file);
            return true;
        }
    }

    /**
     * 目录下是否存在这个文件
     * @param dir
     * @param filename
     * @return
     */
    private Boolean existFile(INode dir, String filename) {
//        if(dir.getChildren() != null && dir.getChildren().size() > 0) {
//            for(INode child : dir.getChildren()) {
//                if(child.getPath().equals(filename)) {
//                    return true;
//                }
//            }
//        }
        return false;
    }


    /**
     * 创建目录
     *
     * @param path 目录路径
     */
    public void mkdir(long txid, String path) {
        // path = /usr/warehouse/hive
        // 你应该先判断一下，“/”根目录下有没有一个“usr”目录的存在
        // 如果说有的话，那么再判断一下，“/usr”目录下，有没有一个“/warehouse”目录的存在
        // 如果说没有，那么就得先创建一个“/warehosue”对应的目录，挂在“/usr”目录下
        // 接着再对“/hive”这个目录创建一个节点挂载上去

        try {
            writeLock();
            maxTxid = txid;
            String[] pathes = path.split("/");
            INodeDirectory parent = dirTree;

            for (String splitedPath : pathes) {
                if (splitedPath.trim().equals("")) {
                    continue;
                }

                INodeDirectory dir = findDirectory(parent, splitedPath);
                if (dir != null) {
                    parent = dir;
                    continue;
                }

                INodeDirectory child = new INodeDirectory(splitedPath);
                parent.addChild(child);

                parent = child;
            }
        } finally {
            writeUnLock();
        }
    }

    /**
     * 对文件目录树查找目录
     *
     * @param dir
     * @param path
     * @return
     */
    private INodeDirectory findDirectory(INodeDirectory dir, String path) {
        if (dir.getChildren().size() == 0) {
            return null;
        }

        for (INode child : dir.getChildren()) {
            if (child instanceof INodeDirectory) {
                INodeDirectory childDir = (INodeDirectory) child;

                if ((childDir.getPath().equals(path))) {
                    return childDir;
                }
            }
        }

        return null;
    }


    /**
     * 代表的是文件目录树中的一个节点
     *
     * @author zhonghuashishan
     */
    private interface INode {

    }

    /**
     * 代表文件目录树中的一个目录
     *
     * @author zhonghuashishan
     */
    public static class INodeDirectory implements INode {

        private String path;
        private List<INode> children;

        public INodeDirectory(String path) {
            this.path = path;
            this.children = new LinkedList<INode>();
        }

        public void addChild(INode inode) {
            this.children.add(inode);
        }

        public String getPath() {
            return path;
        }

        public void setPath(String path) {
            this.path = path;
        }

        public List<INode> getChildren() {
            return children;
        }

        public void setChildren(List<INode> children) {
            this.children = children;
        }

        @Override
        public String toString() {
            return "INodeDirectory{" +
                    "path='" + path + '\'' +
                    ", children=" + children +
                    '}';
        }
    }

    /**
     * 代表文件目录树中的一个文件
     *
     * @author zhonghuashishan
     */
    public static class INodeFile implements INode {

        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "INodeFile{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }

}
